﻿using System;
using SharpDX.Direct2D1;
using SharpDX;
using System.IO;
using sdi = System.Drawing.Imaging;
using d2d = SharpDX.Direct2D1;
using sd = System.Drawing;
using System.Runtime.InteropServices;
using SharpDX.DXGI;

namespace OpenGameEngine.Drawing
{
    public class Bitmap : SharpDX.Direct2D1.Bitmap
    {
        public event EventHandler<EventArgs> Disposed;
        public event EventHandler<EventArgs> Disposing;

        public SizeF DotsPerInch { get { return base.DotsPerInch; } }
        public d2d.Factory Factory { get { return base.Factory; } }
        public Boolean IsDisposed { get { return base.IsDisposed; } }
        public IntPtr NativePointer { get { return base.NativePointer; } }
        public Size PixelSize { get { return base.PixelSize; } }
        public PixelFormat PixelFormat { get { return base.PixelFormat; } }
        public SizeF Size { get { return base.Size; } }

        public Bitmap(Size size, DataPointer pointer, Int32 pitch, BitmapProperties properties)
            : base(Globals.RenderTarget, size, pointer, pitch, properties)
        {
            base.Disposed += new EventHandler<EventArgs>(Bitmap_Disposed);
            base.Disposing += new EventHandler<EventArgs>(Bitmap_Disposing);
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public object MemberwiseClone()
        {
            return base.MemberwiseClone();
        }

        public Type GetType()
        {
            return base.GetType();
        }

        void Bitmap_Disposing(object sender, EventArgs e)
        {
            if (Disposing != null)
                Disposing(sender, e);
        }

        void Bitmap_Disposed(object sender, EventArgs e)
        {
            if (Disposed != null)
                Disposed(sender, e);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        public override string ToString()
        {
            return base.ToString();
        }

        /// <summary>
        /// Loads a Direct2D Bitmap from a file using System.Drawing.Image.FromFile(...)
        /// </summary>
        /// <param name="renderTarget">The render target.</param>
        /// <param name="stream">The stream.</param>
        /// <returns>A D2D1 Bitmap</returns>
        public static Bitmap LoadFromStream(Stream stream)
        {
            // Loads from file using System.Drawing.Image
            using (sd.Bitmap bitmap = (sd.Bitmap)sd.Image.FromStream(stream))
            {
                Rectangle sourceArea = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                BitmapProperties bitmapProperties = new BitmapProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied));
                Size size = new Size(bitmap.Width, bitmap.Height);

                // Transform pixels from BGRA to RGBA
                int stride = bitmap.Width * sizeof(int);
                using (DataStream tempStream = new DataStream(bitmap.Height * stride, true, true))
                {
                    // Lock System.Drawing.Bitmap
                    sdi.BitmapData bitmapData = bitmap.LockBits(sourceArea, sdi.ImageLockMode.ReadOnly, sdi.PixelFormat.Format32bppPArgb);

                    // Convert all pixels 
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        int offset = bitmapData.Stride * y;
                        for (int x = 0; x < bitmap.Width; x++)
                        {
                            // Not optimized 
                            byte B = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte G = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte R = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte A = Marshal.ReadByte(bitmapData.Scan0, offset++);

                            int rgba = B | (G << 8) | (R << 16) | (A << 24);

                            tempStream.Write(rgba);
                        }

                    }
                    bitmap.UnlockBits(bitmapData);
                    tempStream.Position = 0;

                    return new OpenGameEngine.Drawing.Bitmap(size, tempStream, stride, bitmapProperties);
                }
            }
        }

        /// <summary>
        /// Loads a Direct2D Bitmap from a file using System.Drawing.Image.FromFile(...)
        /// </summary>
        /// <param name="renderTarget">The render target.</param>
        /// <param name="stream">The stream.</param>
        /// <param name="transparancyColor">Make the image transparent, null if it doens't have to be transparent</param>
        /// <returns>A D2D1 Bitmap</returns>
        public static Bitmap LoadFromStream(Stream stream, Color transparancyColor)
        {
            // Loads from file using System.Drawing.Image
            using (sd.Bitmap bitmap = (sd.Bitmap)sd.Image.FromStream(stream))
            {
                if (transparancyColor != null)
                    bitmap.MakeTransparent(transparancyColor);

                Rectangle sourceArea = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
                BitmapProperties bitmapProperties = new BitmapProperties(new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied));
                Size size = new Size(bitmap.Width, bitmap.Height);

                // Transform pixels from BGRA to RGBA
                int stride = bitmap.Width * sizeof(int);
                using (DataStream tempStream = new DataStream(bitmap.Height * stride, true, true))
                {
                    // Lock System.Drawing.Bitmap
                    sdi.BitmapData bitmapData = bitmap.LockBits(sourceArea, sdi.ImageLockMode.ReadOnly, sdi.PixelFormat.Format32bppPArgb);

                    // Convert all pixels 
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        int offset = bitmapData.Stride * y;
                        for (int x = 0; x < bitmap.Width; x++)
                        {
                            // Not optimized 
                            byte B = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte G = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte R = Marshal.ReadByte(bitmapData.Scan0, offset++);
                            byte A = Marshal.ReadByte(bitmapData.Scan0, offset++);

                            int rgba = B | (G << 8) | (R << 16) | (A << 24);

                            tempStream.Write(rgba);
                        }

                    }
                    bitmap.UnlockBits(bitmapData);
                    tempStream.Position = 0;

                    return new OpenGameEngine.Drawing.Bitmap(size, tempStream, stride, bitmapProperties);
                }
            }
        }
    }
}
